﻿using System;
using LinqToAmazonSL.AWS;
using System.Collections.Generic;
using System.Threading;
using System.Globalization;
using System.Linq.Expressions;

namespace LinqToAmazonSL
{
    public class AmazonHelper
    {
        private string amazonKey { get; set; }

        private AWSECommerceServicePortTypeClient AmazonWS = new AWSECommerceServicePortTypeClient();
        private AutoResetEvent timer = new AutoResetEvent(false);
        private ItemSearchResponse internalResponse;

        public AmazonHelper()
        {
            AmazonWS.ItemSearchCompleted += new EventHandler<ItemSearchCompletedEventArgs>(AmazonWS_ItemSearchCompleted);
        }

        //Function to store the AmazonKey
        public void SetAmazonKey(string value)
        {
            this.amazonKey = value;
        }

        public bool ValidateAmazonKey()
        { 
            ItemSearch validateSearch = new ItemSearch();
            validateSearch.AWSAccessKeyId = amazonKey;
            ItemSearchRequest request = new ItemSearchRequest(){ Title = "Harry Potter" };
            validateSearch.Request = new ItemSearchRequest[1]{ request };

            try
            {
                AmazonWS.ItemSearchAsync(validateSearch);
                timer.WaitOne();
            }
            catch (Exception ex)
            {
                throw new AppDomainUnloadedException("Unable to connect to Amazon web service" + ex.Message);
            }

            if (internalResponse != null &&
                internalResponse.Items[0].Request.Errors != null &&
                internalResponse.Items[0].Request.Errors[0].Code.Equals("AWS.InvalidParameterValue"))
            {
                return false;
            }

            return true;
        }

        private void DoRequest(ItemSearch itemSearch)
        {
            string errorLog = string.Empty;
            try
            {
                AmazonWS.ItemSearchAsync(itemSearch);
                //Wait for response
                timer.WaitOne();
            }
            catch (Exception ex)
            {
                throw new AppDomainUnloadedException("Unable to connect to Amazon web service" + ex.Message);
            }
            //Check for errors
            if (internalResponse != null && internalResponse.OperationRequest.Errors != null )
            {
                foreach (var error in internalResponse.OperationRequest.Errors)
                    errorLog += error.Message + "\n";   
            }
            if (internalResponse != null && internalResponse.Items[0].Request.Errors != null && internalResponse.Items[0].Request.Errors[0].Code.Equals("AWS.InvalidParameterValue"))
            {
                throw new UnauthorizedAccessException("Unable to connect to Amazon web service because AWSAccessKey");
            }
            if (errorLog.Length!=0)
                throw new InvalidOperationException(errorLog);
        }

        internal ItemSearch BuildRequest(AmazonQueryCriteria criteria)
        {
            ItemSearch itemSearch = new ItemSearch();
            itemSearch.AWSAccessKeyId = amazonKey;
            itemSearch.Request = criteria.ToAmazonRequest();

            return itemSearch;
        }

        //Uses the common query with select.
        internal IEnumerable<S> PerformWebQuery<T, S>(ItemSearch itemSearch, LambdaExpression selector) where T : AmazonItem
        {
            bool finish = false;
            int pageNumber = 1;
            while (!finish)
            {
                //Set the pages to bring
                itemSearch.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                itemSearch.Request[1].ItemPage = (pageNumber + 1).ToString(CultureInfo.CurrentCulture);
                //Send query to Web Service
                DoRequest(itemSearch);
                //Process the response, it cames in two pages
                for (int i = 0; i < 2; i++)
                {
                    if (internalResponse != null &&
                        internalResponse.Items.Length > 0 &&
                        internalResponse.Items[i] != null &&
                        internalResponse.Items[i].Item != null)
                    {
                        foreach (var item in internalResponse.Items[i].Item)
                        {
                            T obj = (T)Activator.CreateInstance(typeof(T));
                            obj.LoadFromAmazonResultItem(item);
                            var retObj = selector.Compile().DynamicInvoke(new object[] { obj });
                            yield return (S)retObj;
                        }
                        //Finished with the page. Now I go to the next one.
                        pageNumber++;
                    }
                    else   //There are no items left so the serch is stopped
                        finish = true;
                }
            }
            yield break;
        }

        //Uses the query without select.
        internal IEnumerable<T> PerformWebQuery<T>(ItemSearch itemSearch) where T : AmazonItem
        {
            bool finish = false;
            int pageNumber = 1;
            while (!finish)
            {
                //Set the pages to bring
                itemSearch.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
               
                //Send query to Web Service
                DoRequest(itemSearch);
                //process the response
                
                if (internalResponse != null &&
                    internalResponse.Items.Length > 0 &&
                    internalResponse.Items[0] != null &&
                    internalResponse.Items[0].Item != null)
                {
                    foreach (var item in internalResponse.Items[0].Item)
                    {
                        T obj = (T)Activator.CreateInstance(typeof(T));
                        obj.LoadFromAmazonResultItem(item);
                        yield return obj;
                    }
                    //Finished with the page. Now I go to the next one.
                    pageNumber++;
                }
                else   //There are no items left so the serch is stopped
                    finish = true;
            }
            yield break;
        }

        void AmazonWS_ItemSearchCompleted(object sender, ItemSearchCompletedEventArgs e)
        {
            internalResponse = e.Result;
            timer.Set();
        }

        //Function used by a "Query" an operation "Any" in the end
        internal bool PerformBoolQuery(ItemSearch request, GuideRequest dr)
        {
            string paramName = "";
            request.Request[0].ItemPage = dr.PageBase.ToString(CultureInfo.CurrentCulture);

            DoRequest(request);

            bool State = false;
            if (internalResponse != null &&
                internalResponse.Items.Length > 0 &&
                internalResponse.Items[0] != null)
            {
                //If the result is not empty, the variable "State" is true
                State = (internalResponse.Items[0].Item[dr.ItemPageBase] != null);
            }
            else if (internalResponse == null)
            {
                paramName = "Collection null:";
                throw new ArgumentNullException(paramName + "internalResponse");
            }
            return State;
        }

        //Function used by a "Query" an operation "Count" in the end
        internal object PerformCountQuery(ItemSearch request, GuideRequest dr)
        {
            string paramName = "";
            request.Request[0].ItemPage = dr.PageBase.ToString(CultureInfo.CurrentCulture);

            DoRequest(request);

            if (internalResponse != null &&
                internalResponse.Items.Length > 0 &&
                internalResponse.Items[0] != null && internalResponse.Items[0].Item != null)
            {
                if (int.Parse(internalResponse.Items[0].TotalResults,CultureInfo.CurrentCulture) > Int32.MaxValue)
                {
                    throw new OverflowException("Too many results");
                }
                else
                {
                    //If you apply a "Take" and the value of the "Take" is less than the total results, return the "Count" value
                    if ((dr.Count != -1) && (dr.Count < int.Parse(internalResponse.Items[0].TotalResults,CultureInfo.CurrentCulture)))
                    {
                        return dr.Count;
                    }
                    //If you apply a "Take" and the value of the "Take" is greater than the total results, return the "totalResults"
                    else if (dr.Count > int.Parse(internalResponse.Items[0].TotalResults,CultureInfo.CurrentCulture))
                    {
                        return int.Parse(internalResponse.Items[0].TotalResults,CultureInfo.CurrentCulture);
                    }
                    else
                    {
                        return (Convert.ToInt32(internalResponse.Items[0].TotalResults,CultureInfo.CurrentCulture) - ((dr.PageBase * 10) - 10 + dr.ItemPageBase));
                    }
                }
            }
            else
                if (internalResponse == null)
                {
                    paramName = "Collection Null";
                    throw new ArgumentNullException(paramName+":internalResponse");
                }
            return 0;
        }

        internal object PerformWebQuery<T>(ItemSearch request, Oper oper, GuideRequest dr, LambdaExpression selector) where T : AmazonItem
        {
            //It is a PerformWebQuery that will return only one item for the cases of First, Last, etc                    
            int pageNumber = dr.PageBase;
            string paramName = "";
            int Pages = 0, Dif = 0, Results = 0;
            switch (oper)
            {
                case Oper.Last:
                    //Updates by "dr", the page to find and choose the first item
                    Pages = dr.PageBase + (dr.Count - 1) / 10;
                    if (dr.Count > 0)
                    {	//If the "Query" had a "Take" concatenated
                        Dif = dr.ItemPageBase + (dr.Count - 1) % 10;
                        if (Dif >= 10)
                        {
                            Pages++;
                            Dif = Dif % 10;
                        }
                        request.Request[0].ItemPage = Pages.ToString(CultureInfo.CurrentCulture);
                        DoRequest(request);
                        if (internalResponse.Items != null)
                        {
                            Results = int.Parse(internalResponse.Items[0].TotalResults,CultureInfo.CurrentCulture);
                            //If it was applied a "Take" by an amount greater than "totalResults," I bring the last no matter what the "Count"
                            if (dr.PageBase * 10 - 10 + dr.ItemPageBase + dr.Count > Results)
                            {
                                Pages = (Results - 1) / 10;
                                Dif = (Results - 1) % 10;
                                request.Request[0].ItemPage = Pages.ToString(CultureInfo.CurrentCulture);
                                DoRequest(request);
                                var item = internalResponse.Items[0].Item[Dif];
                                T obj = (T)Activator.CreateInstance(typeof(T));
                                obj.LoadFromAmazonResultItem(item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { obj });
                                return obj;
                            }
                            else
                            {//If it was applied a "Take" by an amount not greater than "totalResults," I bring the last allowing the "Count"
                                if (Results != 0)
                                {
                                    var item = internalResponse.Items[0].Item[Dif];
                                    T obj = (T)Activator.CreateInstance(typeof(T));
                                    obj.LoadFromAmazonResultItem(item);
                                    if (selector != null)
                                        return selector.Compile().DynamicInvoke(new object[] { obj });
                                    return obj;
                                }
                                else
                                {
                                    paramName = "Empty collection:";
                                    throw new InvalidOperationException(paramName+"Results");
                                }
                            }
                        }
                        else
                        {
                            paramName = "Collection null:";
                            throw new ArgumentNullException(paramName + "internalResponse.Items");
                        }
                    }
                    else
                    {//If the "Query" did not have a "Take" concatenated
                        request.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                        DoRequest(request);
                        if (internalResponse.Items != null)
                        {
                            Pages = int.Parse(internalResponse.Items[0].TotalPages,CultureInfo.CurrentCulture);
                            Results = int.Parse(internalResponse.Items[0].TotalResults,CultureInfo.CurrentCulture);
                            if (Results != 0)
                            { //I get the last allowed by "totalResult"
                                Dif = Results - ((Pages - 1) * 10);
                                request.Request[0].ItemPage = (Pages).ToString(CultureInfo.CurrentCulture);
                                DoRequest(request);
                                var Item = internalResponse.Items[0].Item[Dif - 1];
                                T Obj = (T)Activator.CreateInstance(typeof(T));
                                Obj.LoadFromAmazonResultItem(Item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { Obj });
                                return Obj;
                            }
                            else
                            {
                                paramName = "Empty collection:";
                                throw new InvalidOperationException(paramName+"Results");
                            }
                        }
                        else
                        {
                            paramName = "Collection null";
                            throw new ArgumentNullException(paramName+"internalResponse.Items");
                        }
                    }
                case Oper.LastOrDefault:
                    //Updates by "dr", the page to find and choose the first item
                    Pages = dr.PageBase + (dr.Count - 1) / 10;
                    if (dr.Count > 0)
                    {//If the "Query" had a "Take" concatenated
                        Dif = dr.ItemPageBase + (dr.Count - 1) % 10;
                        if (Dif >= 10)
                        {
                            Pages++;
                            Dif = Dif % 10;
                        }
                        request.Request[0].ItemPage = Pages.ToString(CultureInfo.CurrentCulture);
                        DoRequest(request);
                        if (internalResponse.Items != null)
                        {
                            Results = int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture);
                            //If it was applied a "Take" by an amount greater than "totalResults," I bring the last no matter what the "Count"
                            if (dr.PageBase * 10 - 10 + dr.ItemPageBase + dr.Count > Results)
                            {
                                Pages = (Results - 1) / 10;
                                Dif = (Results - 1) % 10;
                                request.Request[0].ItemPage = Pages.ToString(CultureInfo.CurrentCulture);
                                DoRequest(request);
                                var item = internalResponse.Items[0].Item[Dif];
                                T obj = (T)Activator.CreateInstance(typeof(T));
                                obj.LoadFromAmazonResultItem(item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { obj });
                                return obj;
                            }
                            else
                            {//If it was applied a "Take" by an amount not greater than "totalResults," I bring the last allowing the "Count"
                                if (Results != 0)
                                {
                                    var item = internalResponse.Items[0].Item[Dif];
                                    T obj = (T)Activator.CreateInstance(typeof(T));
                                    obj.LoadFromAmazonResultItem(item);
                                    if (selector != null)
                                        return selector.Compile().DynamicInvoke(new object[] { obj });
                                    return obj;
                                }
                                else
                                {
                                    return null;
                                }
                            }
                        }
                        else
                        {
                            paramName = "Collection null";
                            throw new ArgumentNullException(paramName+"internalResponse.Items");
                        }
                    }
                    else
                    {//If the "Query" did not have a "Take" concatenated
                        request.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                        DoRequest(request);
                        if (internalResponse.Items != null)
                        {
                            Pages = int.Parse(internalResponse.Items[0].TotalPages, CultureInfo.CurrentCulture);
                            Results = int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture);
                            if (Results != 0)
                            {//I get the last allowed by "totalResult"
                                Dif = Results - ((Pages - 1) * 10);
                                request.Request[0].ItemPage = (Pages).ToString(CultureInfo.CurrentCulture);
                                DoRequest(request);
                                var Item = internalResponse.Items[0].Item[Dif - 1];
                                T Obj = (T)Activator.CreateInstance(typeof(T));
                                Obj.LoadFromAmazonResultItem(Item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { Obj });
                                return Obj;
                            }
                            else
                            {
                                return null;
                            }
                        }
                        else
                        {
                            paramName = "Collection null";
                            throw new ArgumentNullException(paramName+"internalResponse.Items");
                        }
                    }
                case Oper.First:
                    request.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                    DoRequest(request);
                    if (internalResponse.Items != null)
                    {//I got the first allowed by "dr"
                        Results = int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture);
                        if (Results != 0)
                        {
                            var item = internalResponse.Items[0].Item[dr.ItemPageBase];
                            T obj = (T)Activator.CreateInstance(typeof(T));
                            obj.LoadFromAmazonResultItem(item);
                            if (selector != null)
                                return selector.Compile().DynamicInvoke(new object[] { obj });
                            return obj;
                        }
                        else
                        {
                            paramName = "Invalid Operation";
                            throw new InvalidOperationException(paramName+"Results");
                        }
                    }
                    else
                    {
                        paramName = "Collection null";
                        throw new ArgumentNullException(paramName+"internalResponse.Items");
                    }
                case Oper.FirstOrDefault:
                    request.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                    DoRequest(request);
                    if (internalResponse.Items != null)
                    {//I got the first allowed by "dr"
                        Results = int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture);
                        var item = internalResponse.Items[0].Item[dr.ItemPageBase];
                        T obj = (T)Activator.CreateInstance(typeof(T));
                        obj.LoadFromAmazonResultItem(item);
                        if (selector != null)
                            return selector.Compile().DynamicInvoke(new object[] { obj });
                        return obj;
                    }
                    else
                    {
                        paramName = "Collection null";
                        throw new ArgumentNullException(paramName+"internalResponse.Items");
                    }
            }
            throw new NotImplementedException();

        }

        internal object PerformWebQuery<T>(ItemSearch Req, Oper Op, string Val, GuideRequest dr, LambdaExpression selector) where T : AmazonItem
        {
            //It is a PerformWebQuery that will return only one item for the cases of ElementAt or ElementAtOrDeafult
            int pageNumber = dr.PageBase;
            int Results;
            int Page = dr.PageBase;
            string paramName = "";
            int Target;
            switch (Op)
            {
                case Oper.ElementAt:
                    Target = int.Parse(Val,CultureInfo.CurrentCulture);
                    if (!(dr.Count > 0))
                    {//If the "Query" had a "Take" concatenated
                        Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                        DoRequest(Req);
                        if (internalResponse.Items != null)
                        {
                            Results = int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture);
                            if ((Target >= 0) && (Results - (dr.PageBase * 10 - 10 + dr.ItemPageBase) >= 0))
                            {//Getting selected item, only if this does not exceed the maximum allowed by "totalResults"
                                Req.Request[0].ItemPage = Page.ToString(CultureInfo.CurrentCulture);
                                DoRequest(Req);
                                var item = internalResponse.Items[0].Item[dr.ItemPageBase];
                                T obj = (T)Activator.CreateInstance(typeof(T));
                                obj.LoadFromAmazonResultItem(item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { obj });
                                return obj;
                            }
                            else
                            {
                                paramName = "Position out of range:";
                                throw new ArgumentOutOfRangeException(paramName+"Target");
                            }
                        }
                        else
                        {
                            paramName = "Collection null:";
                            throw new ArgumentNullException(paramName+"internalResponse.Items");
                        }
                    }
                    else
                    {//If the "Query" did not have a "Take" Concatenated
                        Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                        DoRequest(Req);
                        if (internalResponse.Items != null)
                        {
                            Results = dr.Count - 1;
                            if ((Target >= 0) && (Results - Target >= 0))
                            {//Getting selected item, only if this does not exceed the maximum allowed by "dr.Count"
                                Req.Request[0].ItemPage = Page.ToString(CultureInfo.CurrentCulture);
                                DoRequest(Req);
                                var item = internalResponse.Items[0].Item[dr.ItemPageBase];
                                T obj = (T)Activator.CreateInstance(typeof(T));
                                obj.LoadFromAmazonResultItem(item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { obj });
                                return obj;
                            }
                            else
                            {
                                paramName = "Position out of range:";
                                throw new ArgumentOutOfRangeException(paramName+"Target");
                            }
                        }
                        else
                        {
                            paramName = "Collection null";
                            throw new ArgumentNullException(paramName+"internalResponse.Items");
                        }
                    }
                case Oper.ElementAtOrDefault:
                    Target = int.Parse(Val, CultureInfo.CurrentCulture);
                    if (!(dr.Count > 0))
                    {//If the "Query" had a "Take" concatenated
                        Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                        DoRequest(Req);
                        if (internalResponse.Items != null)
                        {//Getting selected item, only if this does not exceed the maximum allowed by "totalResults"
                            Results = int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture);
                            if ((Target >= 0) && (Results - (dr.PageBase * 10 - 10 + dr.ItemPageBase) >= 0))
                            {
                                Req.Request[0].ItemPage = Page.ToString(CultureInfo.CurrentCulture);
                                DoRequest(Req);
                                var item = internalResponse.Items[0].Item[dr.ItemPageBase];
                                T obj = (T)Activator.CreateInstance(typeof(T));
                                obj.LoadFromAmazonResultItem(item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { obj });
                                return obj;
                            }
                            else
                            {
                                return null;
                            }
                        }
                        else
                        {
                            paramName = "Collection null";
                            throw new ArgumentNullException(paramName+"internalResponse.Items");
                        }
                    }
                    else
                    {//If the "Query" did not have a "Take" Concatenated
                        Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                        DoRequest(Req);
                        if (internalResponse.Items != null)
                        {
                            Results = dr.Count - 1;
                            if ((Target >= 0) && (Results - Target >= 0))
                            {//Getting selected item, only if this does not exceed the maximum allowed by "dr.Count"
                                Req.Request[0].ItemPage = Page.ToString(CultureInfo.CurrentCulture);
                                DoRequest(Req);
                                var item = internalResponse.Items[0].Item[dr.ItemPageBase];
                                T obj = (T)Activator.CreateInstance(typeof(T));
                                obj.LoadFromAmazonResultItem(item);
                                if (selector != null)
                                    return selector.Compile().DynamicInvoke(new object[] { obj });
                                return obj;
                            }
                            else
                            {
                                return null;
                            }
                        }
                        else
                        {
                            paramName="Collection null";
                            throw new ArgumentNullException(paramName+"internalResponse.Items");
                        }
                    }
            }
            throw new NotImplementedException();
        }

        internal IEnumerable<S> PerformWebQueryCollection<T, S>(ItemSearch Req, Oper Op, string Val, GuideRequest dr, LambdaExpression selector) where T : AmazonItem
        {
            //It is a PerformWebQuery used for the cases of Take or Skip with selector
            int pageNumber = dr.PageBase;
            int target;
            bool finish;
            switch (Op)
            {
                case Oper.Take:
                    target = dr.Count;
                    if (target <= 0)
                        yield break;
                    else
                    {
                        int itemsReturned = 0;
                        finish = false;
                        int Base = dr.ItemPageBase;
                        while (!finish)
                        {
                            //Set the page to bring
                            Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                            //Send query to Web Service
                            DoRequest(Req);
                            if (internalResponse != null &&
                                internalResponse.Items.Length > 0 &&
                                internalResponse.Items[0] != null)
                            {
                                //Traveling the number of results on the page that I need
                                while (internalResponse.Items[0].Item != null && Base < internalResponse.Items[0].Item.Length && itemsReturned < target)
                                {
                                    T obj = (T)Activator.CreateInstance(typeof(T));
                                    obj.LoadFromAmazonResultItem(internalResponse.Items[0].Item[Base]);
                                    yield return (S)selector.Compile().DynamicInvoke(new object[] { obj });

                                    Base++;
                                    itemsReturned++;
                                }
                                if (internalResponse.Items[0].Item == null)
                                    finish = true;
                                else
                                {

                                    if (Base == internalResponse.Items[0].Item.Length && itemsReturned < target && pageNumber < Convert.ToInt32(internalResponse.Items[0].TotalPages))
                                    //Finished the page and there are more results left. I go the next page.
                                    {
                                        pageNumber++;
                                        Base = 0;
                                    }
                                    else
                                        //There are no more item left or I already obtained the amount needed. Stop the serch.
                                        finish = true;
                                }
                            }
                            //There are no items left so the serch is stopped
                            else
                                finish = true;
                        }
                        yield break;
                    }
                case Oper.Skip:
                    target = int.Parse(Val, CultureInfo.CurrentCulture);
                    int itemsToSkip;
                    if (dr.Count > 0)
                    {
                        pageNumber = dr.PageBase;
                        itemsToSkip = dr.ItemPageBase;
                        finish = false;
                        while (!finish)
                        {
                            //Set the page to bring
                            Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                            //Send query to Web Service
                            DoRequest(Req);
                            if (internalResponse != null &&
                                internalResponse.Items.Length > 0 &&
                                internalResponse.Items[0] != null &&
                                internalResponse.Items[0].Item != null)
                            {
                                int index;
                                if (target < 0)
                                    index = 0;
                                else
                                    index = itemsToSkip;
                                //	Traveling the number of results on the page
                                while ((index < 10) &&
                                    ((pageNumber * 10 - 10 + index) < (dr.Count + dr.PageBase * 10 - 10 + dr.ItemPageBase))
                                    && (internalResponse.Items[0].Item[index] != null))
                                {
                                    T obj = (T)Activator.CreateInstance(typeof(T));
                                    obj.LoadFromAmazonResultItem(internalResponse.Items[0].Item[index]);
                                    yield return (S)selector.Compile().DynamicInvoke(new object[] { obj });

                                    index++;
                                }
                                if ((index == 10) && (pageNumber < ((dr.Count - 1) / 10 + 1)))
                                {
                                    //Finished the page and there are more results left. I go the next page.
                                    pageNumber++;
                                    itemsToSkip = 0;
                                }
                                else
                                    //There are no more item left. Stop the serch.
                                    finish = true;
                            }
                            //There are no more item left so I stop the serch.
                            else
                                finish = true;
                        }
                    }
                    else
                    {
                        pageNumber = dr.PageBase;
                        itemsToSkip = dr.ItemPageBase;

                        finish = false;

                        while (!finish)
                        {
                            //Set the pages to bring
                            Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                            //Send query to Web Service
                            DoRequest(Req);
                            if (internalResponse != null &&
                                internalResponse.Items.Length > 0 &&
                                internalResponse.Items[0] != null &&
                                internalResponse.Items[0].Item != null)
                            {
                                if (target < int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture))
                                {
                                    int index;
                                    if (target < 0)
                                        index = 0;
                                    else
                                        index = itemsToSkip;
                                    //	Traveling the number of results on the page
                                    while (index < internalResponse.Items[0].Item.Length)
                                    {
                                        T obj = (T)Activator.CreateInstance(typeof(T));
                                        obj.LoadFromAmazonResultItem(internalResponse.Items[0].Item[index]);
                                        yield return (S)selector.Compile().DynamicInvoke(new object[] { obj });
                                        index++;
                                    }
                                    if (pageNumber < Convert.ToInt32(internalResponse.Items[0].TotalPages))
                                    {
                                        //Finished the page and there are more results left. I go the next page.
                                        pageNumber++;
                                        itemsToSkip = 0;
                                    }
                                    else
                                        //There are no more item left. Stop the serch.
                                        finish = true;
                                }
                                else
                                {
                                    throw new ArgumentOutOfRangeException("Skip out of range");
                                }
                            }
                            //There are no more item left so I stop the serch.
                            else
                                finish = true;
                        }
                    }
                    yield break;
            }
        }

        internal IEnumerable<T> PerformWebQueryCollection<T>(ItemSearch Req, Oper Op, string Val, GuideRequest dr) where T : AmazonItem
        {
            //It is a PerformWebQuery use for the cases of Take or Skip without selector
            int pageNumber = dr.PageBase;
            int target;
            bool finish;
            switch (Op)
            {
                case Oper.Take:
                    target = dr.Count;
                    if (target <= 0)
                        yield break;
                    else
                    {
                        int itemsReturned = 0;
                        finish = false;
                        int Base = dr.ItemPageBase;
                        while (!finish)
                        {
                            //Set the pages to bring
                            Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);

                            //Send query to Web Service
                            DoRequest(Req);
                            if (internalResponse != null &&
                                internalResponse.Items.Length > 0 &&
                                internalResponse.Items[0] != null)
                            {
                                //Traveling the number of results on the page that I need
                                while (internalResponse.Items[0].Item != null && Base < internalResponse.Items[0].Item.Length && itemsReturned < target)
                                {
                                    T obj = (T)Activator.CreateInstance(typeof(T));
                                    obj.LoadFromAmazonResultItem(internalResponse.Items[0].Item[Base]);
                                    yield return obj;

                                    Base++;
                                    itemsReturned++;
                                }

                                if (internalResponse.Items[0].Item == null)
                                    finish = true;
                                else
                                {
                                    if (Base == internalResponse.Items[0].Item.Length && itemsReturned < target && pageNumber < Convert.ToInt32(internalResponse.Items[0].TotalPages))
                                    //Finished the page and there are more results left. I go the next page.
                                    {
                                        pageNumber++;
                                        Base = 0;
                                    }
                                    else
                                        //There are no more item left or I already obtained the amount needed. Stop the serch.
                                        finish = true;
                                }
                            }
                            //There are no more item so I stop the serch.
                            else
                                finish = true;
                        }
                        yield break;
                    }
                case Oper.Skip:
                    target = int.Parse(Val, CultureInfo.CurrentCulture);
                    int itemsToSkip;
                    if (dr.Count > 0)
                    {
                        pageNumber = dr.PageBase;
                        itemsToSkip = dr.ItemPageBase;

                        finish = false;

                        while (!finish)
                        {
                            //Set the pages to bring
                            Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                            //Send query to Web Service
                            DoRequest(Req);
                            if (internalResponse != null &&
                                internalResponse.Items.Length > 0 &&
                                internalResponse.Items[0] != null &&
                                internalResponse.Items[0].Item != null)
                            {
                                int index;
                                if (target < 0)
                                    index = 0;
                                else
                                    index = itemsToSkip;
                                //Traveling the number of results on the page
                                while ((index < 10) &&
                                    ((pageNumber * 10 - 10 + index) < (dr.Count + dr.PageBase * 10 - 10 + dr.ItemPageBase)) 
                                    && (internalResponse.Items[0].Item[index] != null))
                                {
                                    T obj = (T)Activator.CreateInstance(typeof(T));
                                    obj.LoadFromAmazonResultItem(internalResponse.Items[0].Item[index]);
                                    yield return obj;
                                    index++;
                                }
                                if ((index == 10) && (pageNumber < ((dr.Count - 1) / 10 + 1)))
                                {
                                    //Finished the page and there are more results left. I go the next page.
                                    pageNumber++;
                                    itemsToSkip = 0;
                                }
                                else
                                    //There are no more item left. Stop the serch.
                                    finish = true;
                            }
                            //There are no more item left so I stop the serch.
                            else
                                finish = true;
                        }
                    }
                    else
                    {
                        pageNumber = dr.PageBase;
                        itemsToSkip = dr.ItemPageBase;

                        finish = false;

                        while (!finish)
                        {
                            //Set the pages to bring
                            Req.Request[0].ItemPage = pageNumber.ToString(CultureInfo.CurrentCulture);
                            //Send query to Web Service
                            DoRequest(Req);
                            if (internalResponse != null &&
                                internalResponse.Items.Length > 0 &&
                                internalResponse.Items[0] != null &&
                                internalResponse.Items[0].Item != null)
                            {
                                if (target < int.Parse(internalResponse.Items[0].TotalResults, CultureInfo.CurrentCulture))
                                {
                                    int index;
                                    if (target < 0)
                                        index = 0;
                                    else
                                        index = itemsToSkip;
                                    //Traveling the number of results on the page
                                    while (index < internalResponse.Items[0].Item.Length)
                                    {
                                        T obj = (T)Activator.CreateInstance(typeof(T));
                                        obj.LoadFromAmazonResultItem(internalResponse.Items[0].Item[index]);
                                        yield return obj;
                                        index++;
                                    }
                                    if (pageNumber < Convert.ToInt32(internalResponse.Items[0].TotalPages))
                                    {
                                        //Finished the page and there are more results left. I go the next page.
                                        pageNumber++;
                                        itemsToSkip = 0;
                                    }
                                    else
                                        //There are no more item left. Stop the serch.
                                        finish = true;
                                }
                                else
                                {
                                    finish = true;
                                    //throw new ArgumentOutOfRangeException("Skip out of range");
                                }
                            }
                            //There are no more item left so I stop the serch.
                            else
                                finish = true;
                        }
                    }
                    yield break;
            }
        }
    }
}
